﻿using System;
using System.IO;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 数据源
    /// </summary>
    unsafe public class DataSource : IDisposableZ
    {
        /// <summary>
        /// 原始数据长度
        /// </summary>
        private long? _originDataLength = null;
        /// <summary>
        /// 获取原始数据长度
        /// </summary>
        public long OriginDataLength
        {
            get
            {
                if (_originDataLength.HasValue)
                {
                    return _originDataLength.Value;
                }
                else
                {
                    throw new InvalidOperationException("原始数据长度无效");
                }
            }
        }

        private readonly DataSourceType _dataType;
        /// <summary>
        /// 数据存放类型
        /// </summary>
        public DataSourceType DataType
        {
            get { return this._dataType; }
        }

        private readonly byte[] _bytes = null;
        /// <summary>
        /// 内存数据
        /// </summary>
        public byte[] Bytes
        {
            get
            {
                if (this._dataType == DataSourceType.Bytes)
                {
                    return this._bytes;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.Bytes.ToString()}");
                }
            }
        }


        private readonly string _filePath = null;
        /// <summary>
        /// 文件数据
        /// </summary>
        public string FilePath
        {
            get
            {
                if (this._dataType == DataSourceType.File)
                {
                    return this._filePath;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.File.ToString()}");
                }
            }
        }

        private readonly Stream _stream;
        /// <summary>
        /// 数据流
        /// </summary>
        public Stream Stream
        {
            get
            {
                if (this._dataType == DataSourceType.Stream)
                {
                    return this._stream;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.Stream.ToString()}");
                }
            }
        }

        private readonly BinaryReader _reader;
        /// <summary>
        /// 二进制读取器
        /// </summary>
        public BinaryReader Reader
        {
            get
            {
                if (this._dataType == DataSourceType.BinaryReader)
                {
                    return this._reader;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.BinaryReader.ToString()}");
                }
            }
        }


        private readonly byte* _dataPtr = null;
        /// <summary>
        /// 数据指针
        /// </summary>
        public byte* DataPtr
        {
            get
            {
                if (this._dataType == DataSourceType.DataPtr)
                {
                    return this._dataPtr;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.DataPtr.ToString()}");
                }
            }
        }




        private readonly IDataSourceExtend _extend;
        /// <summary>
        /// 扩展数据
        /// </summary>
        public IDataSourceExtend Extend
        {
            get
            {
                if (this._dataType == DataSourceType.Extend)
                {
                    return this._extend;
                }
                else
                {
                    throw new InvalidOperationException($"当前数据源类型不是{DataSourceType.Extend.ToString()}");
                }
            }
        }





        private int? _offset = null;
        /// <summary>
        /// 数据起始偏移位置,小于等于0从头开始
        /// </summary>
        public int Offset
        {
            get
            {
                if (this._offset.HasValue)
                {
                    return this._offset.Value;
                }
                else
                {
                    if (this._longOffset.HasValue)
                    {
                        throw new InvalidOperationException($"数据起始偏移值大于了{int.MaxValue},请使用\"{nameof(LongOffset)}\"字段");
                    }
                    else
                    {
                        throw new InvalidOperationException("数据起始偏移位置值无效");
                    }
                }
            }
        }

        private long? _longOffset = null;
        /// <summary>
        /// 数据起始偏移位置,小于等于0从头开始
        /// </summary>
        public long LongOffset
        {
            get
            {
                if (this._longOffset.HasValue)
                {
                    return this._longOffset.Value;
                }
                else
                {
                    throw new InvalidOperationException("数据起始偏移位置值无效");
                }
            }
        }




        private int? _length = null;
        /// <summary>
        /// 数据长度，小于0到结尾
        /// </summary>
        public int Length
        {
            get
            {
                if (this._length.HasValue)
                {
                    return this._length.Value;
                }
                else
                {
                    if (this._longLength.HasValue)
                    {
                        throw new InvalidOperationException($"数据长度大于了{int.MaxValue},请使用\"{nameof(LongLength)}\"字段");
                    }
                    else
                    {
                        throw new InvalidOperationException("数据长度值无效");
                    }
                }
            }
        }

        private long? _longLength = null;
        /// <summary>
        /// 数据长度，小于0到结尾
        /// </summary>
        public long LongLength
        {
            get
            {
                if (this._longLength.HasValue)
                {
                    return this._longLength.Value;
                }
                else
                {
                    throw new InvalidOperationException("数据长度值无效");
                }
            }
        }


        /// <summary>
        /// 指示偏移量和长度值是否有效,有效返回true,无效返回false
        /// </summary>
        public bool OffsetAndLengthHasValid
        {
            get
            {
                if (this._longOffset.HasValue && this._longLength.HasValue)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }



        /// <summary>
        /// 获取或设置与数据源关联的用户定义的数据
        /// </summary>
        public object Tag { get; set; }




        /// <summary>
        /// 默认构造函数
        /// </summary>
        /// <param name="dataType"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <param name="originDataLength"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        private DataSource(DataSourceType dataType, int offset, int length, int originDataLength)
        {
            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset > originDataLength)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{offset}\"无效,超出范围");
            }

            if (length < 0)
            {
                length = originDataLength - offset;
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._dataType = dataType;

            this._offset = offset;
            this._longOffset = offset;
            this._length = length;
            this._longLength = length;

            this._originDataLength = originDataLength;
        }

        /// <summary>
        /// 默认构造函数
        /// </summary>
        /// <param name="dataType"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <param name="originDataLength"></param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        private DataSource(DataSourceType dataType, long offset, long length, long originDataLength)
        {
            if (offset < 0)
            {
                offset = 0;
            }
            else if (offset > originDataLength)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{offset}\"无效,超出范围");
            }

            if (length < 0)
            {
                length = originDataLength - offset;
            }

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._dataType = dataType;


            if (offset <= int.MaxValue)
            {
                this._offset = (int)offset;
            }
            this._longOffset = offset;

            if (length <= int.MaxValue)
            {
                this._length = (int)length;
            }
            this._longLength = length;

            this._originDataLength = originDataLength;
        }

#if !NET4_0

        private Memory<byte> _memoryBuffer = null;
        /// <summary>
        /// 内存数据
        /// </summary>
        public Memory<byte> MemoryBuffer
        {
            get { return _memoryBuffer; }
        }

        /// <summary>
        /// 构建数据源-内存Memory_byte
        /// </summary>
        /// <param name="memoryBuffer">Memory内存缓存buffer数据</param>
        public DataSource(Memory<byte> memoryBuffer)
            : this(DataSourceType.Memory, 0, memoryBuffer.Length, memoryBuffer.Length)
        {
            this._memoryBuffer = memoryBuffer;
            this._offset = 0;
            this._longOffset = 0;
            this._length = memoryBuffer.Length;
            this._longLength = memoryBuffer.Length;
            this._originDataLength = memoryBuffer.Length;
        }

#endif

        /// <summary>
        /// 构建数据源-内存byte[]
        /// </summary>
        /// <param name="buffer">内存缓存buffer数据</param>
        public DataSource(byte[] buffer)
        : this(DataSourceType.Bytes, 0, buffer.Length, buffer.Length)
        {
            this._bytes = buffer;
        }

        /// <summary>
        /// 构建数据源-内存byte[]
        /// </summary>
        /// <param name="buffer">内存缓存buffer数据</param>
        /// <param name="offset">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSource(byte[] buffer, int offset, int length)
        : this(DataSourceType.Bytes, offset, length, buffer.Length)
        {
            this._bytes = buffer;
        }

        /// <summary>
        /// 构建数据源-内存byte[]
        /// </summary>
        /// <param name="buffer">内存缓存buffer数据</param>
        /// <param name="offset">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSource(byte[] buffer, long offset, long length)
        : this(DataSourceType.Bytes, offset, length, buffer.LongLength)
        {
            this._bytes = buffer;
        }

        /// <summary>
        /// 构建文件数据源-文件
        /// 如果length小于0，在打开文件时需要调用UpdateFileBufferLength方法,否则数据源对象中的数据长度值将无效
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="offset">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSource(string filePath, long offset = -1, long length = -1)
        {
            this._dataType = DataSourceType.File;
            this._filePath = filePath;

            if (offset < 0)
            {
                offset = 0;
            }

            if (offset <= int.MaxValue)
            {
                this._offset = (int)offset;
            }
            this._longOffset = offset;

            if (length > 0)
            {
                if (length <= int.MaxValue)
                {
                    this._length = (int)length;
                }
                this._longLength = length;
                this._originDataLength = length;
            }
        }

        /// <summary>
        /// 构建数据源-数据流
        /// </summary>
        /// <param name="stream">数据流</param>
        /// <param name="offset">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSource(Stream stream, long offset = -1, long length = -1)
            : this(DataSourceType.Stream, offset, length, stream.Length)
        {
            this._stream = stream;
        }

        /// <summary>
        /// 构建数据源-二进制读取器
        /// </summary>
        /// <param name="reader">二进制读取器</param>
        /// <param name="offset">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public DataSource(BinaryReader reader, long offset = -1, long length = -1)
            : this(DataSourceType.BinaryReader, offset, length, reader.BaseStream.Length)
        {
            this._reader = reader;
        }

        /// <summary>
        /// 构建数据源-数据指针
        /// </summary>
        /// <param name="ptr">数据指针</param>
        /// <param name="length">数据长度</param>
        public DataSource(byte* ptr, int length)
            : this(DataSourceType.DataPtr, 0, length, length)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"数据长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._dataPtr = ptr;
        }

        /// <summary>
        /// 构建数据源-数据指针
        /// </summary>
        /// <param name="ptr">数据指针</param>
        /// <param name="length">数据长度</param>
        public DataSource(byte* ptr, long length)
            : this(DataSourceType.DataPtr, 0, length, length)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), $"数据长度值\"{length}\"无效,超出范围,不能小于0");
            }

            this._dataPtr = ptr;
        }

        /// <summary>
        /// 构建数据源-扩展数据
        /// </summary>
        /// <param name="data">扩展数据</param>
        /// <param name="offset">数据起始偏移位置</param>
        /// <param name="length">数据长度</param>
        public DataSource(IDataSourceExtend data, long offset, long length)
            : this(DataSourceType.Extend, offset, length, length)
        {
            this._extend = data;
        }








        /// <summary>
        /// 更新文件Buffer长度
        /// </summary>
        /// <param name="fileLength">文件大小</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public void UpdateFileBufferLength(long fileLength)
        {
            if (this._dataType != DataSourceType.File)
            {
                throw new InvalidOperationException("仅数据源类型为文件时，才允许调用当前方法，其它缓存类型不允许调用");
            }

            if (fileLength < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(fileLength), "文件数据长度不能小于0");
            }

            if (this._offset > fileLength)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置\"{this._offset}\"无效,超出范围");
            }

            this._longLength = fileLength - this._longOffset.Value;
            if (this._longLength <= int.MaxValue)
            {
                this._length = (int)this._longLength;
            }

            this._originDataLength = fileLength;
        }

        /// <summary>
        /// 更新数据的有效位置和长度
        /// </summary>
        /// <param name="offset">数据起始偏移位置,小于0无效</param>
        /// <param name="length">数据长度</param>
        public void Update(int offset, int length)
        {
            if (this._originDataLength.HasValue)
            {
                if (offset < 0 || offset > this._originDataLength.Value)
                {
                    throw new ArgumentOutOfRangeException(nameof(offset));
                }

                if (length < 0 || (long)offset + (long)length > this._originDataLength.Value)
                {
                    throw new ArgumentOutOfRangeException(nameof(length));
                }
            }

            this._offset = offset;
            this._longOffset = offset;
            this._length = length;
            this._longLength = length;
        }

        /// <summary>
        /// 更新数据的有效位置和长度
        /// </summary>
        /// <param name="offset">数据起始偏移位置,小于0无效</param>
        /// <param name="length">数据长度</param>
        public void Update(long offset, long length)
        {
            if (this._originDataLength.HasValue)
            {
                if (offset < 0 || offset > this._originDataLength.Value)
                {
                    throw new ArgumentOutOfRangeException(nameof(offset));
                }

                if (length < 0 || offset + length > this._originDataLength.Value)
                {
                    throw new ArgumentOutOfRangeException(nameof(length));
                }
            }

            if (offset <= int.MaxValue)
            {
                this._offset = (int)offset;
            }
            else
            {
                this._offset = null;
            }
            this._longOffset = offset;

            if (length <= int.MaxValue)
            {
                this._length = (int)length;
            }
            else
            {
                this._length = null;
            }
            this._longLength = length;
        }





        private IDisposable _disposabler = null;
        /// <summary>
        /// 附加需要释放的资源
        /// </summary>
        /// <param name="disposabler"></param>
        public void AttatchDisposabler(IDisposable disposabler)
        {
            this._disposabler = disposabler;
        }


        /// <summary>
        /// 当前对象释放标识[true:已释放;false:未释放]
        /// </summary>
        protected bool _disposed = false;
        /// <summary>
        /// 获取当前对象是否已释放[true:已释放;false:未释放]
        /// </summary>
        public bool Disposed
        {
            get { return _disposed; }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            if (this._disposed)
            {
                return;
            }
            this._disposed = true;

            if (this._disposabler != null)
            {
                this._disposabler.Dispose();
                this._disposabler = null;
            }

            this.Dispose(true);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {

        }
    }


    /// <summary>
    /// 
    /// </summary>
    unsafe public static class DataSourceEx
    {
        /// <summary>
        /// 将DataSpan中的数据写入到BinaryWriter中
        /// </summary>
        /// <param name="writer">BinaryWriter</param>
        /// <param name="data">要写入的数据</param>
        /// <exception cref="NotSupportedException"></exception>
        public static void Write(this BinaryWriter writer, DataSource data)
        {
            if (data == null || data.Length <= 0)
            {
                return;
            }

            byte[] buffer = null;
            switch (data.DataType)
            {
#if !NET4_0
                case DataSourceType.Memory:
                    writer.Write(data.MemoryBuffer.Span);
                    break;
#endif
                case DataSourceType.Bytes:
                    writer.Write(data.Bytes, data.Offset, data.Length);
                    break;
                case DataSourceType.Stream:
                    WriteStream(writer, data.Stream, data.LongLength, data.LongLength);
                    break;
                case DataSourceType.File:
                    using (var fs = new FileStream(data.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        WriteStream(writer, fs, data.LongLength, data.LongLength);
                    }
                    break;
                case DataSourceType.BinaryReader:
                    buffer = data.Reader.ReadBytes(data.Length);
                    writer.Write(buffer);
                    break;
                case DataSourceType.DataPtr:
                    writer.Write(new Span<byte>(data.DataPtr, data.Length));
                    break;
                case DataSourceType.Extend:
                    buffer = new byte[data.Extend.Length];
                    data.Extend.Read(buffer, 0, buffer.Length, 0);
                    writer.Write(buffer);
                    break;
                default:
                    throw new NotSupportedException($"不支持有类型数据:{data.DataType.ToString()}");
            }
        }

        private static void WriteStream(BinaryWriter writer, Stream stream, long offset, long length)
        {
            byte[] buffer;
            if (length < 4096)
            {
                buffer = new byte[(int)length];
            }
            else
            {
                buffer = new byte[4096];
            }

            stream.Position = offset;
            long endPos = offset + length, mod;
            int readLength = buffer.Length;
            while (stream.Position <= endPos)
            {
                mod = endPos - stream.Position;
                if (mod < buffer.Length)
                {
                    readLength = (int)mod;
                }

                stream.Read(buffer, 0, readLength);
                writer.Write(buffer, 0, readLength);
            }
        }



        /// <summary>
        /// 将数据从BinaryReader读取到DataSpan对象中
        /// </summary>
        /// <param name="reader">BinaryReader</param>
        /// <param name="data">要读取的数据</param>
        /// <param name="length">要读取的数据长度</param>
        public static void Read(this BinaryReader reader, DataSource data, int length)
        {
            if (data == null || data.Length <= 0)
            {
                return;
            }

            if (data.Bytes == null || data.Bytes.Length < length)
            {
                throw new InvalidOperationException($"数据源中的数组长度不足");
            }
            else
            {
                reader.Read(data.Bytes, 0, length);
                data.Update(0, length);
            }
        }
    }

}
